﻿namespace OnlineVideos.RingToneEditor
{
    using System;
    using System.IO;
    using System.Runtime.CompilerServices;

    public class Mp3Frame
    {
        private static readonly int[,,] bitRates = new int[,,] { { { 0, 0x20, 0x40, 0x60, 0x80, 160, 0xc0, 0xe0, 0x100, 0x120, 320, 0x160, 0x180, 0x1a0, 0x1c0 }, { 0, 0x20, 0x30, 0x38, 0x40, 80, 0x60, 0x70, 0x80, 160, 0xc0, 0xe0, 0x100, 320, 0x180 }, { 0, 0x20, 40, 0x30, 0x38, 0x40, 80, 0x60, 0x70, 0x80, 160, 0xc0, 0xe0, 0x100, 320 } }, { { 0, 0x20, 0x30, 0x38, 0x40, 80, 0x60, 0x70, 0x80, 0x90, 160, 0xb0, 0xc0, 0xe0, 0x100 }, { 0, 8, 0x10, 0x18, 0x20, 40, 0x30, 0x38, 0x40, 80, 0x60, 0x70, 0x80, 0x90, 160 }, { 0, 8, 0x10, 0x18, 0x20, 40, 0x30, 0x38, 0x40, 80, 0x60, 0x70, 0x80, 0x90, 160 } } };
        private bool crcPresent;
        private const int MaxFrameLength = 0x4000;
        private static readonly int[] sampleRatesVersion1 = new int[] { 0xac44, 0xbb80, 0x7d00 };
        private static readonly int[] sampleRatesVersion2 = new int[] { 0x5622, 0x5dc0, 0x3e80 };
        private static readonly int[] sampleRatesVersion25 = new int[] { 0x2b11, 0x2ee0, 0x1f40 };
        private static readonly int[,] samplesPerFrame = new int[,] { { 0x180, 0x480, 0x480 }, { 0x180, 0x480, 0x240 } };

        public Mp3Frame()
        {
        }

        private static bool IsValidHeader(byte[] headerBytes, Mp3Frame frame)
        {
            if ((headerBytes[0] == 0xff) && ((headerBytes[1] & 0xe0) == 0xe0))
            {
                frame.MpegVersion = (MpegVersion) ((headerBytes[1] & 0x18) >> 3);
                if (frame.MpegVersion == MpegVersion.Reserved)
                {
                    return false;
                }
                frame.MpegLayer = (MpegLayer) ((headerBytes[1] & 6) >> 1);
                if (frame.MpegLayer == MpegLayer.Reserved)
                {
                    return false;
                }
                int num = (frame.MpegLayer == MpegLayer.Layer1) ? 0 : ((frame.MpegLayer == MpegLayer.Layer2) ? 1 : 2);
                frame.crcPresent = (headerBytes[1] & 1) == 0;
                int num2 = (headerBytes[2] & 240) >> 4;
                if (num2 == 15)
                {
                    return false;
                }
                int num3 = (frame.MpegVersion == MpegVersion.Version1) ? 0 : 1;
                frame.BitRate = bitRates[num3, num, num2] * 0x3e8;
                if (frame.BitRate == 0)
                {
                    return false;
                }
                int index = (headerBytes[2] & 12) >> 2;
                if (index == 3)
                {
                    return false;
                }
                if (frame.MpegVersion == MpegVersion.Version1)
                {
                    frame.SampleRate = sampleRatesVersion1[index];
                }
                else if (frame.MpegVersion ==MpegVersion.Version2)
                {
                    frame.SampleRate = sampleRatesVersion2[index];
                }
                else
                {
                    frame.SampleRate = sampleRatesVersion25[index];
                }
                bool flag = (headerBytes[2] & 2) == 2;
                bool flag2 = (headerBytes[2] & 1) == 1;
                frame.ChannelMode = (ChannelMode) ((headerBytes[3] & 0xc0) >> 6);
                int num5 = (headerBytes[3] & 0x30) >> 4;
                bool flag3 = (headerBytes[3] & 8) == 8;
                bool flag4 = (headerBytes[3] & 4) == 4;
                int num6 = headerBytes[3] & 3;
                int num7 = flag ? 1 : 0;
                frame.SampleCount = samplesPerFrame[num3, num];
                int num8 = frame.SampleCount / 8;
                if (frame.MpegLayer == MpegLayer.Layer1)
                {
                    frame.FrameLength = (((num8 * frame.BitRate) / frame.SampleRate) + num7) * 4;
                }
                else
                {
                    frame.FrameLength = ((num8 * frame.BitRate) / frame.SampleRate) + num7;
                }
                if (frame.FrameLength > 0x4000)
                {
                    return false;
                }
                return true;
            }
            return false;
        }

        public static Mp3Frame LoadFromStream(Stream input)
        {
            return LoadFromStream(input, true);
        }

        public static Mp3Frame LoadFromStream(Stream input, bool readData)
        {
            byte[] buffer = new byte[4];
            if (input.Read(buffer, 0, buffer.Length) < buffer.Length)
            {
                return null;
            }
            Mp3Frame frame = new Mp3Frame();
            while (!IsValidHeader(buffer, frame))
            {
                buffer[0] = buffer[1];
                buffer[1] = buffer[2];
                buffer[2] = buffer[3];
                if (input.Read(buffer, 3, 1) < 1)
                {
                    return null;
                }
            }
            int count = frame.FrameLength - 4;
            if (readData)
            {
                frame.RawData = new byte[frame.FrameLength];
                Array.Copy(buffer, frame.RawData, 4);
                if (input.Read(frame.RawData, 4, count) < count)
                {
                    throw new EndOfStreamException("Unexpected end of stream before frame complete");
                }
                return frame;
            }
            input.Position += count;
            return frame;
        }

        public int BitRate { get; private set; }

        public ChannelMode ChannelMode { get; private set; }

        public int FrameLength { get; private set; }

        public MpegLayer MpegLayer { get; private set; }

        public MpegVersion MpegVersion { get; private set; }

        public byte[] RawData { get; private set; }

        public int SampleCount { get; private set; }

        public int SampleRate { get; private set; }
    }
}

